package com.jingdianjichi.auth.domain.service.impl;

import cn.dev33.satoken.secure.SaSecureUtil;
import cn.dev33.satoken.stp.SaTokenInfo;
import cn.dev33.satoken.stp.StpUtil;
import com.google.gson.Gson;
import com.jingdianjichi.auth.common.enums.AuthUserStatusEnum;
import com.jingdianjichi.auth.common.enums.IsDeletedFlagEnum;
import com.jingdianjichi.auth.domain.constants.AuthConstant;
import com.jingdianjichi.auth.domain.convert.AuthUserConverter;
import com.jingdianjichi.auth.domain.entity.AuthUserBO;
import com.jingdianjichi.auth.domain.service.AuthUserDomainService;
import com.jingdianjichi.auth.domain.redis.RedisUtil;
import com.jingdianjichi.auth.infra.basic.entity.*;
import com.jingdianjichi.auth.infra.basic.service.*;
import lombok.SneakyThrows;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

@Service
public class AuthUserDomainServiceImpl implements AuthUserDomainService {

    @Resource
    private AuthUserService authUserService;

    @Resource
    private AuthRoleService authRoleService;

    @Resource
    private AuthUserRoleService authUserRoleService;

    @Resource
    private TransactionTemplate transactionTemplate;        //手动事务提交我们后面学习缓存的时候用

    @Resource
    private AuthPermissionService permissionService;

    @Resource
    private AuthRolePermissionService rolePermissionService;

    //加盐算法
    private String salt="chiken";

    //验证码前缀
    private static final String LOGIN_PREFIX="loginCode";


    //用工具类去交互
    @Resource
    private RedisUtil redisUtil;

    //权限,我们是隔离的这两个,我们在存入Redis的时候也要这样存储的
    private String authPermissionPrefix="auth.permission";

    //角色
    private String authRolePrefix="auth.role";

    @Override
    @SneakyThrows
    @Transactional
    public Boolean register(AuthUserBO authUserBO) {
        //因为如果我们是登录调用这个接口的话没有问题,但是如果是别的接口掉的话,不是像登录一样只有userName的,所以我们放在最前面去做
        AuthUser existAuthUser = new AuthUser();
        existAuthUser.setUserName(authUserBO.getUserName());

        //校验用户是否存在解决重复注册的问题
        List<AuthUser> existUser = authUserService.queryByCondition(existAuthUser);

        if (existUser.size() > 0){

            return true;
        }

        AuthUser authUser=AuthUserConverter.INSTANCE.convertBOtoEntity(authUserBO);


        //我们这里就使用MD5加盐算法
        if(StringUtils.isNotBlank(authUser.getPassword())){
            //因为我们通过公众号注册的话,是没有呢密码这个东西的,所以判断一下
            authUser.setPassword(SaSecureUtil.md5BySalt(authUser.getPassword(),salt));
        }

        authUser.setStatus(AuthUserStatusEnum.OPEN.getCode());
        authUser.setIsDeleted(IsDeletedFlagEnum.UN_DELETED.getCode());

        //添加用户
        Integer count = authUserService.insert(authUser);

        /*
            后续会去建立角色关联,把当前用户的角色和权限刷到Redis中
         */

        //建立初步的角色关联
        AuthRole authRole = new AuthRole();
        authRole.setRoleKey(AuthConstant.NORMAL_USERR);

        //把这个角色的id查出来
        AuthRole roleResult=authRoleService.queryByCondition(authRole);
        Long roleId = roleResult.getId();
        Long userId = authUser.getId();     //记得开逐渐回显

        AuthUserRole authUserRole = new AuthUserRole();
        authUserRole.setRoleId(roleId);
        authUserRole.setUserId(userId);

        authUserRoleService.insert(authUserRole);

        //把数据写入缓存
        String roleKey = redisUtil.buildKey(authRolePrefix, authUser.getUserName());    //这个userName其实是唯一的账号来的

        //我们想缓存中存入一个实体,这一年如果扩展的话是你非常的方便的
        List<AuthRole> roleList=new ArrayList<>();
        roleList.add(authRole);

        //gson序列化
        redisUtil.set(roleKey,new Gson().toJson(roleList));     //放角色

        //根据role查一下相关的权限是啥
        AuthRolePermission authRolePermission = new AuthRolePermission();
        authRolePermission.setRoleId(roleId);

        setPermissionToRedis(authRolePermission,authUser.getUserName());
        /*

            //我们不去进行耦合,这个mapper就查自己的表
            List<AuthRolePermission> rolePermissionList=rolePermissionService.queryByCondition(authRolePermission);

            //收集roleID对应的所有的PermissionId
            List<Long> permissionIdList = rolePermissionList.stream().map(AuthRolePermission::getPermissionId).collect(Collectors.toList());

            //根据permissionId查权限的key
            List<AuthPermission> permissionList = permissionService.queryByRoleList(permissionIdList);

            String permissionKey = redisUtil.buildKey(authPermissionPrefix, authUser.getUserName());

            //序列化
            redisUtil.set(permissionKey, new Gson().toJson(permissionList));
         */
        return count>0;
    }



    //获得用户的权限并放入缓存
    private void setPermissionToRedis(AuthRolePermission authRolePermission ,String userName){

        //我们不去进行耦合,这个mapper就查自己的表
        List<AuthRolePermission> rolePermissionList=rolePermissionService.queryByCondition(authRolePermission);

        //收集roleID对应的所有的PermissionId
        List<Long> permissionIdList = rolePermissionList.stream().map(AuthRolePermission::getPermissionId).collect(Collectors.toList());

        //根据permissionId查权限的key
        List<AuthPermission> permissionList = permissionService.queryByRoleList(permissionIdList);

        String permissionKey = redisUtil.buildKey(authPermissionPrefix,userName);

        //序列化
        redisUtil.set(permissionKey, new Gson().toJson(permissionList));
    }

    //跟新用户
    @Override
    public Boolean update(AuthUserBO authUserBO) {
        AuthUser authUser = AuthUserConverter.INSTANCE.convertBOtoEntity(authUserBO);
        Integer count = authUserService.update(authUser);
        authUser.setStatus(AuthUserStatusEnum.OPEN.getCode());
        authUser.setIsDeleted(IsDeletedFlagEnum.UN_DELETED.getCode());

        //如果有任何的更新都要和缓存进行同步的

        return count >0;
    }

    //删除用户
    @Override
    public Boolean delete(AuthUserBO authUserBO) {
        AuthUser authUser = new AuthUser();
        authUser.setId(authUserBO.getId());
        authUser.setIsDeleted(IsDeletedFlagEnum.DELETED.getCode());

        Integer count = authUserService.update(authUser);

        //也去和缓存同步的
        return count>0;
    }


    //注册用户,我们目前就提供公众号注册这一个方法
    @Override
    public SaTokenInfo doLogin(String validCode) {
        String loginKey = redisUtil.buildKey(LOGIN_PREFIX, validCode);

        //用户的账号user_name
        String openId = redisUtil.get(loginKey);

        if (StringUtils.isBlank(openId)){
            //如果是空的,直接返回空
            return null;
        }

        AuthUserBO authUserBO = new AuthUserBO();
        authUserBO.setUserName(openId);

        //注册用户
        this.register(authUserBO);

        //登录老弟
        StpUtil.login(openId);

        //返回令牌
        SaTokenInfo tokenInfo = StpUtil.getTokenInfo();

        return tokenInfo;
    }

    //查询用户
    @Override
    public AuthUserBO getUSerInfo(AuthUserBO authUserBO) {
        AuthUser authUser=new AuthUser();
        authUser.setUserName(authUserBO.getUserName());

        List<AuthUser> userList = authUserService.queryByCondition(authUser);

        //但是这个情况一般不会出现,我们做一些容错的处理
        if (CollectionUtils.isEmpty(userList)){
            return new AuthUserBO();
        }

        AuthUser user = userList.get(0);

        AuthUserBO authUserBOResult=AuthUserConverter.INSTANCE.convertEntityToBo(user);

        return authUserBOResult;
    }


    //批量获得用户数据
    @Override
    public List<AuthUserBO> listUserInfoByIds(List<String> userNameList)
    {
        //批量查询
        List<AuthUser> userList = authUserService.listUserInfoByIds(userNameList);

        if (CollectionUtils.isEmpty(userList)) {
            return Collections.emptyList();
        }

        return AuthUserConverter.INSTANCE.convertEntityToBO(userList);
    }

}
